哈囉,我們又見面了,前幾天我們把整個 Django
網站搬到雲端了,今天我們來研究一下 RESTful API,到底什麼樣的 API 才叫做 RESTful
呢 ? 讓我們繼續看下去。
REST
?REST
的全名是 Representational State Transfer,而這套 REST 架構是由 Roy Fielding
在 2000年
的博士論文提出,恩,有點老舊了,不過沒關係,畢竟它還是經典,而 RESTful API
,表示符合 Representational State Transfer 架構的 API 們,就叫做 RESTful API
。
在我寫這篇文章的時候,發現有個新興的開 API 的方法叫做 GraphQL,之後再來瞭解,今天就先看看經典且傳統的 REST
架構。
怕等等有太多專有名詞,要先來個簡介,什麼是 API
,和 serve-client
架構,以及 CRUD
操作,先看看短片吧,短片會替我解釋 XD
建議瞭解這些基本的專有名詞之後,你再往下面看下去,不然你應該會看得霧煞煞。
REST
來了其實,人人都會開 API 或使用 API,那麼到底有沒有 REST
,差在哪裡 ? RESTful API
又應該長什麼樣子呢 ? 符合哪些條件,才可以算是真正的 RESTful API
呢 ?
REST
這六條件的順序不影響,前五條是必須遵守才叫做真正的 REST
,而第六條可視情況選擇。
但其實沒有符合真正的 REST 也無所謂,就像寫程式不一定要套用 Design Pattern、也不一定要遵守 SOLID 原則,但這些原則和架構,就是用來把 "痛過的" 經驗 SOP 化,告訴世人這樣做比較好。
原文:Once a developer becomes familiar with one of your APIs, he should be able to follow similar approach for other APIs.
( 開發者只要熟悉你的一支 API,他就應該能用同樣的邏輯,來理解你開出來的其他 API )
舉例來說,我開出一支 GET http://rs.com/api/articles
,可以拿到 RS 寫的所有文章,那麼你可以期望,我會有另一支 API 是 GET http://rs.com/api/articles/1/title
,可以拿到 id=1 的文章標題,照這樣的邏輯,那麼可能就會有 GET http://rs.com/api/articles/1/content/
,可以拿到 id=1 這篇文章的內文。
把這三隻 API 抓出來看,你會發現風格一致,且邏輯也一致,這樣就符合第一點 Uniform interface
的條件,再新增幾條風格一致的 API 一起看更有感覺。
GET http://rs.com/api/articles
GET http://rs.com/api/articles/1/title
GET http://rs.com/api/articles/1/content
DELETE http://rs.com/api/articles/1
POST http://rs.com/api/articles
Resource-based (以 資源
為基礎)
透過 RESTful API,我們可以把資源(可能是 圖片、文字、用戶資料 等等)傳送給 client端
,而且可以是多指向一,也就是允許多個 API 指向同一個資源,舉例來說,一個 API 長這樣 GET http://rs.com/articles/1
,那麼它代表的是可以拿到我的 id=1 的文章,而 DELETE http://rs.com/articles/1
就是代表要刪掉我的 id=1 的文章。
Representations (把 資源
呈現的方式)
透過 RESTful API,Server端
把 資源 以 JSON
或 XML
的形式呈現,假設有一個 Article 的資料,client端
透過 GET http://rs.com/articles/1
,而 server端
回傳一個 JSON
的資料
{
"id": 1,
"title": "What is RESTful API?",
"content": "哈囉,我們又見面了,...",
"created": "2020-03-01 10:03:33",
"updated": "2020-03-01 22:12:57"
}
No client context shall be stored on the server between requests. The client is responsible for managing the state of the application.
( 在
server端
不儲存client端
的狀態資訊 (context),而是由client端
來掌握 app 的狀態 )
需要先解釋 context
,如果你把 context 丟翻譯,會出現 語境
、語意
、上下文
等等讓人摸不著頭緒的字眼,我在開發 Android
時,跟 Android 的 context 相處了好久,才弄懂 context 的意思,但弄懂之後,才發現這個詞到哪邊都是通用的。
簡單來說,在這邊所指的 context
包含了你在網站進行的任何行為,你點了一個按鈕是一個 context、瀏覽一個頁面是一個 context、登入帳號是一個 context、結帳下單是一個 context。
原文所提到的 server端
不應該儲存 client
context,這麼一說,難道我不用登入就可以下單了嗎 ?! 其實不是,這涉略到 authentication
(驗證) 和 authorization
(授權),可以參考 (2015) How to understand “RESTful API is stateless”? 這篇,有我覺得蠻好理解的解釋,關於 authentication,所有能夠證明用戶身分的資訊,必須包含在一個 request 裡,而不是由 server 儲存資訊。
以上面自己做的圖來解釋,如果有保存狀態的後端架構下,同時又做了 load balance 機制的例子,如果今天有個客戶來我的電商網站登入了,然後下單一項商品,當他下單的時候發現跳出一個訊息說「請登入,才能下單」,心裡一定會很不爽,因為明明登入過了,卻還要再登入一次,那有可能是因為 Server1
儲存了身份驗證的狀態,可是真的要下單時,由於有設定 load balance 的機制,可能下單的 request 導向了另一台 Server2
,Server2
卻不知道客戶在 Server1
所做的事情做到哪裡了。
以上的例子有點像是 打客服專線
,你每次打客服專線,可能都是不同的客服人員接聽,每個不同的客服人員,不會知道你在上一通,跟前一位客服人員講了什麼。
那麼代表我的每個 request,都要包含用戶的帳號密碼嗎? 這樣做有點蠢,所以就有其他的驗證方式出現,例如可以把帳號密碼驗證之後,轉成 token 方式,token 可能包含加密過的帳號密碼、過期時間,有各種 token,像是Basic authentication、Json Web Token(JWT
)、Bearer token。
Stateless
是有優點的,這代表狀態不會被儲存在你的任何一個伺服器上,這樣你的伺服器就可以擴大增長,變成分散式,依舊可以參考 (2015) How to understand “RESTful API is stateless”?
原文: Servers and clients may also be replaced and developed independently, as long as the interface between them is not altered.
( 只要中間的 API 沒有變,Server 和 Client 可以個別開發,相互不受影響 )
這很好理解,也就是說 RESTful API 開出來之後,只要 API 不動,你的 client端
應該要可以不受 server端
的開發推進所影響;同理,你的 server端
應該要不受 client端
的開發推進影響。
原文: Well-managed caching partially or completely eliminates some client-server interactions, further improving scalability and performance.
( 好的 Caching 能部分或完全消滅 client 和 server 間的互動,更能夠改善 scalability 和 performance )
這點也很好理解,當你把資源拆成獨立的部分,就能針對不同資源做暫存,看有哪些常用的圖片 或 常被瀏覽的頁面,就把這些常用的資源,獨立出來放著,這樣當重複被使用時,就直接從暫存裡面拿出來就好,不需要再透過 server 到資料庫裡面去撈。
原文: REST allows you to use a layered system architecture where you deploy the APIs on server A, and store data on server B and authenticate requests in Server C, for example. A client cannot ordinarily tell whether it is connected directly to the end server, or to an intermediary along the way.
( REST 允許使用分層的系統架構,舉例來說,你把 API 放在 ServerA、資料放在 ServerB、處理驗證在 ServerC,對於
client端
而言,無法分辨到底是跟哪一個 Server 在對話 )
也就是說,應該要把 功能(講 功能
不太對,但我不知道要用什麼詞QQ) 拆成多層,每層各司其職,也要能夠重複利用各個層的功能,補充說明可以參考 (2015) Layered system constraint in REST API | StackOverflow。
以上面自己做的圖為例,一個有分層的 Server,分成了 DB Server
、Authentication Server
、Web Server
,而 Client端
可能有 Web client
和 Mobile client
,其中 Web client
會用到三個 Server,然而 Mobile client
它不需要 Web server
的資料呈現方式,那麼因為 Server 有分層的關係,Mobile
可以只跟 DB Server
和 Authentication Server
以 RESTful API 要資料即可,可重複利用這些 Server 的 RESTful API,所以不需因為 Mobile
不需要某一個部分,而更動到 Server 的系統架構。
原文:Well, this constraint is optional. Most of the time, you will be sending the static representations of resources in the form of XML or JSON. But when you need to, you are free to return executable code to support a part of your application, e.g., clients may call your API to get a UI widget rendering code. It is permitted.
( 多數情況下是由 server 傳靜態資源給 client,但如果有需要,也可以傳可執行的程式碼給 client 去執行 )
你如果有看過一些網站的原始碼,有些網站會在 html
用上 <script>
的標籤,就會大概理解這條規則的意思,這些 <script>
的標籤內的程式碼,就是當你瀏覽網頁時會執行的 javascript
。如果是沒有特殊廣告或是動畫的部落格,只是純粹呈現文字的文章的話,那麼就不會用到動態執行 javascript
的功能,所以作者才會說,這條規則可以選擇性的使用。
其實寫這篇花的時間比我想像的還要長,需要自己想一些比較貼近我所理解的 REST 的例子,還有把 圖做出來比較好理解。然後找 REST
相關資料的時候,發現中文文章的解釋都蠻零碎的,很難以系統性來理解 REST,通常逛完一圈,得到的只有 REST
的 格式,但其實還不是很懂設計 REST
的核心精神,在找資料的時候,就很想知道採用 REST
架構有什麼優點,最後總結一句「讓人好理解、開發擴充方便」。
不過 REST
也有缺點,如果我今天要透過 API 撈的資料比較複雜,就需要打很多次 API 才能拿到所有的資料,像是需要查詢 文章
與 購買紀錄
的交互關係,那我就需要分別取 文章列表 和 使用者的購買紀錄,如果是更複雜的商業邏輯,則會很浪費網路流量而且速度變慢,所以新的 GraphQL
架構隨之誕生,提供更直覺的資料取用方式。
俗話說的好:「寫 API 是需要美感的」,這種美感需要多看、多做來培養,不是光是這樣看完 REST 六個條件,就知道怎麼開出漂亮的 API 的 XD
我是 RS,這是我的 不做怎麼知道系列 文章,我們 明天見。